package com.boarsoft.common.util;

import java.io.IOException;
import java.util.Arrays;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.boarsoft.common.Util;
import com.boarsoft.serialize.TaggedJsonSerializer;

public class JsonMapping {
	/**
	 * 
	 * @param fromObj
	 * @param fromPath
	 * @param toObj
	 * @param toPath
	 * @param toType
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static Object mapping(Object fromObj, String fromPath, Object toObj, String toPath, String toType)
			throws ClassNotFoundException {
		if (fromObj == null) {
			return null;
		}
		if (toObj == null) {
			throw new IllegalArgumentException("Target object can not be null");
		}
		if (Util.strIsEmpty(fromPath)) {
			throw new IllegalArgumentException("From path can not be null");
		}
		if (Util.strIsEmpty(toPath)) {
			throw new IllegalArgumentException("Target path can not be null");
		}
		Object vo = null;
		if (fromPath.contains("=")) {
			if (fromPath.contains("\n")) {
				vo = TaggedJsonSerializer.deserialize(fromPath);
			} else {
				vo = TaggedJsonSerializer.deserialize1(fromPath);
			}
		} else {
			if (fromObj instanceof JSONObject || fromObj instanceof JSONArray) {
				vo = readJsonObj(fromObj, fromPath.split("\\."));
			}
		}
		if (toType != null) {
			vo = ObjConvertUtil.convert(toType, vo);
		}
		if (toObj instanceof JSONObject || toObj instanceof JSONArray) {
			writeJsonObj(toObj, toPath.split("\\."), vo);
		}
		return toObj;
	}

	/**
	 * 
	 * @param fromObj
	 * @param fromPath
	 * @return
	 */
	public static Object readJsonObj(Object fromObj, String[] fromPath) {
		Object to = fromObj;
		for (int i = 0; i < fromPath.length; i++) {
			String p = fromPath[i];
			if (Util.strIsEmpty(p)) {
				throw new IllegalArgumentException(new StringBuilder(//
						"Invalid mapping from path = ").append(fromPath).toString());
			}
			if ("this".equalsIgnoreCase(p)) {
				to = fromObj;
				continue;
			}
			if (p.contains("[")) {
				String[] a = p.split("\\[");
				JSONObject jo = (JSONObject) to;
				Object fo = jo;// field object
				if (!"this".equals(a[0])) {
					fo = jo.get(a[0]);
				}
				String f = null;
				if (a[1].length() > 1) {
					f = a[1].split("\\]")[0];
				}
				if (fo instanceof JSONObject) {
					to = ((JSONObject) fo).get(f);
				} else if (fo instanceof JSONArray) {
					to = ((JSONArray) fo).get(Integer.parseInt(f));
				}
				continue;
			}
			if (to instanceof JSONObject) {
				to = ((JSONObject) to).get(p);
				continue;
			}
			if (to instanceof JSONArray) {
				JSONArray ja = (JSONArray) to;
				JSONArray ta = new JSONArray();
				for (Object o : ja) {
					if (o != null) {
						if (i < fromPath.length - 1) {
							String pa[] = Arrays.copyOfRange(fromPath, i, fromPath.length);
							o = readJsonObj(o, pa);
						} else {
							if (o instanceof JSONObject) {
								o = ((JSONObject) o).get(p);
							}
						}
					}
					ta.add(o);
				}
				to = ta;
				continue;
			}
		}
		return to;
	}

	/**
	 * 
	 * @param toObj
	 * @param toPath
	 * @param vo
	 */
	public static void writeJsonObj(Object toObj, String[] toPath, Object vo) {
		Object to = null; // temp object
		Object po = toObj; // to 的 parent
		String p = null; // 字段名
		for (int i = 0; i < toPath.length; i++) {
			p = toPath[i];
			if (Util.strIsEmpty(p)) {
				throw new IllegalArgumentException(new StringBuilder(//
						"Invalid mapping target path = ").append(toPath).toString());
			}
			if ("this".equalsIgnoreCase(p)) {
				to = toObj;
				continue;
			}
			if (p.contains("[")) {
				String[] a = p.split("\\[");
				JSONObject jo = (JSONObject) to;
				po = jo;// update parent object
				if (!"this".equals(a[0])) {
					po = jo.get(a[0]);
				}
				String f = null;
				if (a[1].length() > 1) {
					f = a[1].split("\\]")[0];
				}
				if (po == null) {
					if (Util.strIsEmpty(f)) {
						po = new JSONArray();
					} else {
						try {
							Integer.parseInt(f);
							po = new JSONArray();
						} catch (NumberFormatException e) {
							po = new JSONObject();
						}
					}
					jo.put(a[0], po);
					to = new JSONObject();
					if (po instanceof JSONObject) {
						((JSONObject) po).put(f, to);
					} else if (po instanceof JSONArray) {
						JSONArray ta = ((JSONArray) po);
						// 如果后面还有子路径，则递归，直接遍历处理
						if (i < toPath.length - 1) {
							String[] pa = Arrays.copyOfRange(toPath, i + 1, toPath.length);
							if (vo instanceof JSONArray) {
								JSONArray va = ((JSONArray) vo);
								for (Object v : va) {
									writeJsonObj(to, pa, v);
									ta.add(to);
									to = new JSONObject();
								}
								return;
							}
							ta.add(to);
						}
					}
				} else {
					if (po instanceof JSONObject) {
						to = ((JSONObject) po).get(f);
					} else if (po instanceof JSONArray) {
						JSONArray ta = ((JSONArray) po);
						if (Util.strIsEmpty(f)) {
							// 如果后面还有子路径，则递归，直接遍历处理
							if (i < toPath.length - 1) {
								String[] pa = Arrays.copyOfRange(toPath, i + 1, toPath.length);
								if (vo instanceof JSONArray) {
									JSONArray va = ((JSONArray) vo);
									while (ta.size() < va.size()) {
										ta.add(new JSONObject());
									}
									for (int j = 0; j < va.size(); j++) {
										writeJsonObj(ta.get(j), pa, va.get(j));
									}
									return;
								}
								ta.add(to);
							}
						} else {
							int j = Integer.parseInt(f);
							while (ta.size() <= j) {
								ta.add(new JSONObject());
							}
							to = ta.get(j);
						}
					}
				}
				continue;
			}
			if (to instanceof JSONObject) {
				po = to; // update parent object
				to = ((JSONObject) to).get(p);
				if (to == null) {
					if (i < toPath.length) {
						to = new JSONObject();
						((JSONObject) po).put(p, to);
					}
				}
				continue;
			}
		}
		if (po instanceof JSONObject) {
			((JSONObject) po).put(p, vo);
		} else {
			JSONArray ja = ((JSONArray) po);
			if (Util.strIsEmpty(p)) {
				ja.add(vo);
			} else if (vo instanceof JSONArray && p.contains("[")) {
				// ja.clear();
				ja.addAll((JSONArray) vo);
			} else {
				ja.set(Integer.parseInt(p), vo);
			}
		}
	}

	public static void main(String[] args) throws ClassNotFoundException, IOException {
		String fromJson = "{"//
				+ "   request: { custNo: \"001\" }, " //
				+ "   dataMap: { "//
				+ "     user: { name: \"tony\", age: 18 },"//
				+ "     list: [{"//
				+ "       a: { m: 1, n: 3 }, b: [11, 22]"//
				+ "     }, {"//
				+ "       a: { m: 2, n: 4 }, b: [33, 44]"//
				+ "     }]"//
				+ "   }"//
				+ "}";
		String toJson = "{}";
		JSONObject fromObj = JSONObject.parseObject(fromJson);
		JSONObject toObj = JSONObject.parseObject(toJson);

		String fromPath = null;
		String toPath = null;
		String toType = null;

		// fromPath = "this.dataMap[user].age";
		// toPath = "this.data.group[0].users[].age";
		// toType = "java.math.BigDecimal";
		// mapping(fromObj, fromPath, toObj, toPath, toType);
		// System.out.println(toObj);
		//
		// fromPath = "this.dataMap[user].name";
		// toPath = "this.data.group[0].users[5].name";
		// toType = "java.lang.String";
		// mapping(fromObj, fromPath, toObj, toPath, toType);
		// System.out.println(toObj);
		//
		// fromPath = "this.dataMap[list].a.m";
		// toPath = "this.data.list[].b.x";
		// toType = "java.lang.String";
		// System.out.println(readJsonObj(fromObj, fromPath.split("\\.")));
		// mapping(fromObj, fromPath, toObj, toPath, toType);
		// System.out.println(toObj);
		//
		// fromPath = "this.dataMap[list].a.n";
		// toPath = "this.data.list[].b.y";
		// toType = "java.lang.String";
		// System.out.println(readJsonObj(fromObj, fromPath.split("\\.")));
		// mapping(fromObj, fromPath, toObj, toPath, toType);
		// System.out.println(toObj);
		//
		// // fromPath = "java.lang.String=\"abcdefg\"";
		// // fromPath = "java.lang.Float=123.4";
		// fromPath = TaggedJsonSerializer.serialize(123.4f);
		// toPath = "this.data.group";
		// // toType = "java.lang.String";
		// toType = "java.lang.Float";
		// // System.out.println(readJsonObj(fromObj, fromPath.split("\\.")));
		// mapping(fromObj, fromPath, toObj, toPath, toType);
		// System.out.println(toObj);

//		fromPath = "this.dataMap[list]";
//		toPath = "this.data.list";
//		toType = "java.lang.Object";
//		System.out.println(readJsonObj(fromObj, fromPath.split("\\.")));
//		mapping(fromObj, fromPath, toObj, toPath, toType);
//		System.out.println(toObj);

		fromPath = "this.dataMap[list]";
		toPath = "this.data.list[]";
		toType = "java.lang.List";
		System.out.println(readJsonObj(fromObj, fromPath.split("\\.")));
		toObj = JSONObject.parseObject("{\"data\":{\"list\":[{\"a\":{\"m\":1,\"n\":3},\"b\":[11,22]},{\"a\":{\"m\":2,\"n\":4},\"b\":[33,44]}]}}");
		System.out.println(toObj);
		mapping(fromObj, fromPath, toObj, toPath, toType);
		System.out.println(toObj);
		
		String tj = "{ \"list1\": [ 1, 2, 3 ], \"list3\": [] }";
		JSONObject tjo = JSONObject.parseObject(tj);
		String fj = "{ \"list2\": [ 4, 5, 6 ] }";
		JSONObject fjo = JSONObject.parseObject(fj);
		mapping(fjo, "this.list2", tjo, "this.list3[]", "java.lang.List");
		System.out.println(tjo);
	}
}